mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
Merge pull request #12 from learnhouse/feat/editor-improvements
Feat/editor improvements
This commit is contained in:
commit
b3b001a9fd
32 changed files with 384 additions and 221 deletions
|
|
@ -15,7 +15,7 @@ function Element(props: any) {
|
|||
<a target="_blank" rel="noopener noreferrer"> <EyeOpenIcon/></a>
|
||||
</Link>
|
||||
<Link href={`/org/${props.orgslug}/course/${props.courseid}/element/${props.element.id.replace("element_", "")}/edit`}>
|
||||
<a target="_blank" rel="noopener noreferrer"> <Pencil2Icon/></a>
|
||||
<a rel="noopener noreferrer"> <Pencil2Icon/></a>
|
||||
</Link>
|
||||
</ElementWrapper>
|
||||
)}
|
||||
230
front/components/Editor/Editor.tsx
Normal file
230
front/components/Editor/Editor.tsx
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
import React from "react";
|
||||
import { useEditor, EditorContent } from "@tiptap/react";
|
||||
import StarterKit from "@tiptap/starter-kit";
|
||||
import Collaboration from "@tiptap/extension-collaboration";
|
||||
import CollaborationCursor from "@tiptap/extension-collaboration-cursor";
|
||||
import { AuthContext } from "../Security/AuthProvider";
|
||||
import learnhouseIcon from "public/learnhouse_icon.png";
|
||||
import { ToolbarButtons } from "./Toolbar/ToolbarButtons";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import Image from "next/image";
|
||||
import styled from "styled-components";
|
||||
import { getBackendUrl } from "../../services/config";
|
||||
import { GlobeIcon, SlashIcon } from "@radix-ui/react-icons";
|
||||
import Avvvatars from "avvvatars-react";
|
||||
|
||||
interface Editor {
|
||||
content: string;
|
||||
ydoc: any;
|
||||
provider: any;
|
||||
element: any;
|
||||
course: any;
|
||||
setContent: (content: string) => void;
|
||||
}
|
||||
|
||||
function Editor(props: Editor) {
|
||||
const auth: any = React.useContext(AuthContext);
|
||||
|
||||
const editor: any = useEditor({
|
||||
extensions: [
|
||||
StarterKit.configure({
|
||||
// The Collaboration extension comes with its own history handling
|
||||
history: false,
|
||||
}),
|
||||
// Register the document with Tiptap
|
||||
Collaboration.configure({
|
||||
document: props.ydoc,
|
||||
}),
|
||||
// Register the collaboration cursor extension
|
||||
CollaborationCursor.configure({
|
||||
provider: props.provider,
|
||||
user: {
|
||||
name: auth.userInfo.username,
|
||||
color: "#f783ac",
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
content: props.content,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.98 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
key="modal"
|
||||
transition={{
|
||||
type: "spring",
|
||||
stiffness: 360,
|
||||
damping: 70,
|
||||
delay: 0.02,
|
||||
}}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<EditorTop>
|
||||
<EditorDocSection>
|
||||
<EditorInfoWrapper>
|
||||
<EditorInfoLearnHouseLogo width={23} height={23} src={learnhouseIcon} alt="" />
|
||||
<EditorInfoThumbnail src={`${getBackendUrl()}content/uploads/img/${props.course.course.thumbnail}`} alt=""></EditorInfoThumbnail>
|
||||
<EditorInfoDocName>
|
||||
{" "}
|
||||
<b>{props.course.course.name}</b> <SlashIcon /> {props.element.name}{" "}
|
||||
</EditorInfoDocName>
|
||||
<EditorSaveButton onClick={() => props.setContent(editor.getJSON())}>
|
||||
<GlobeIcon></GlobeIcon>
|
||||
</EditorSaveButton>
|
||||
</EditorInfoWrapper>
|
||||
<EditorButtonsWrapper>
|
||||
<ToolbarButtons editor={editor} />
|
||||
</EditorButtonsWrapper>
|
||||
</EditorDocSection>
|
||||
<EditorUsersSection>
|
||||
<EditorUserProfileWrapper>
|
||||
{!auth.isAuthenticated && <span>Loading</span>}
|
||||
{auth.isAuthenticated && <Avvvatars value={auth.userInfo.user_object.user_id} style="shape" />}
|
||||
</EditorUserProfileWrapper>
|
||||
</EditorUsersSection>
|
||||
</EditorTop>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.99 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
key="modal"
|
||||
transition={{
|
||||
type: "spring",
|
||||
stiffness: 360,
|
||||
damping: 70,
|
||||
delay: 0.5,
|
||||
}}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<EditorContentWrapper>
|
||||
<EditorContent editor={editor} />
|
||||
</EditorContentWrapper>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const EditorTop = styled.div`
|
||||
background-color: white;
|
||||
border-radius: 15px;
|
||||
margin: 40px;
|
||||
margin-bottom: 20px;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.03);
|
||||
//position: fixed;
|
||||
z-index: 3;
|
||||
width: -webkit-fill-available;
|
||||
`;
|
||||
|
||||
// Inside EditorTop
|
||||
const EditorDocSection = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
const EditorUsersSection = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
// Inside EditorDocSection
|
||||
const EditorInfoWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 5px;
|
||||
`;
|
||||
const EditorButtonsWrapper = styled.div``;
|
||||
|
||||
// Inside EditorUsersSection
|
||||
const EditorUserProfileWrapper = styled.div`
|
||||
padding-right: 8px;
|
||||
svg {
|
||||
border-radius: 7px;
|
||||
}
|
||||
`;
|
||||
|
||||
// Inside EditorInfoWrapper
|
||||
//..todo
|
||||
const EditorInfoLearnHouseLogo = styled(Image)`
|
||||
border-radius: 6px;
|
||||
margin-right: 15px;
|
||||
`;
|
||||
const EditorInfoDocName = styled.div`
|
||||
font-size: 16px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-left: 10px;
|
||||
color: #494949;
|
||||
|
||||
svg {
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
padding: 3px;
|
||||
color: #353535;
|
||||
}
|
||||
`;
|
||||
|
||||
const EditorSaveButton = styled.div`
|
||||
display: flex;
|
||||
border-radius: 6px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
padding: 5px;
|
||||
font-size: 5px;
|
||||
margin-right: 5px;
|
||||
margin-left: 7px;
|
||||
|
||||
&.is-active {
|
||||
background: rgba(176, 176, 176, 0.5);
|
||||
|
||||
&:hover {
|
||||
background: rgba(139, 139, 139, 0.5);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(217, 217, 217, 0.48);
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
const EditorInfoThumbnail = styled.img`
|
||||
height: 25px;
|
||||
width: 56px;
|
||||
object-fit: cover;
|
||||
object-position: top;
|
||||
border-radius: 7px;
|
||||
margin-left: 5px;
|
||||
`;
|
||||
|
||||
const EditorContentWrapper = styled.div`
|
||||
margin: 40px;
|
||||
margin-top: 20px;
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.03);
|
||||
|
||||
// disable chrome outline
|
||||
|
||||
.ProseMirror {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
padding-bottom: 6px;
|
||||
padding-top: 1px;
|
||||
&:focus {
|
||||
outline: none !important;
|
||||
outline-style: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default Editor;
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
import { default as React, useEffect, useRef } from "react";
|
||||
import { default as React, } from "react";
|
||||
import * as Y from "yjs";
|
||||
import { WebrtcProvider } from "y-webrtc";
|
||||
import EditorWithOptions from "./EditorWithOptions";
|
||||
import { IndexeddbPersistence } from "y-indexeddb";
|
||||
import Editor from "./Editor";
|
||||
import { updateElement } from "../../services/courses/elements";
|
||||
|
||||
interface EditorProps {
|
||||
interface EditorWrapperProps {
|
||||
content: string;
|
||||
element: any;
|
||||
course:any
|
||||
}
|
||||
|
||||
function Editor(props: EditorProps) {
|
||||
function EditorWrapper(props: EditorWrapperProps) {
|
||||
// A new Y document
|
||||
const ydoc = new Y.Doc();
|
||||
const [providerState, setProviderState] = React.useState<any>({});
|
||||
|
|
@ -19,7 +19,6 @@ function Editor(props: EditorProps) {
|
|||
|
||||
function createRTCProvider() {
|
||||
const provider = new WebrtcProvider(props.element.element_id, ydoc);
|
||||
|
||||
setYdocState(ydoc);
|
||||
setProviderState(provider);
|
||||
setIsLoading(false);
|
||||
|
|
@ -29,18 +28,14 @@ function Editor(props: EditorProps) {
|
|||
let element = props.element;
|
||||
element.content = content;
|
||||
const res = await updateElement(element, element.element_id);
|
||||
|
||||
alert(JSON.stringify(res));
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
createRTCProvider();
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<EditorWithOptions content={props.content} setContent={setContent} provider={providerState} ydoc={ydocState}></EditorWithOptions>
|
||||
</div>
|
||||
);
|
||||
return <Editor course={props.course} element={props.element} content={props.content} setContent={setContent} provider={providerState} ydoc={ydocState}></Editor>;
|
||||
}
|
||||
}
|
||||
|
||||
export default Editor;
|
||||
export default EditorWrapper;
|
||||
82
front/components/Editor/Toolbar/ToolbarButtons.tsx
Normal file
82
front/components/Editor/Toolbar/ToolbarButtons.tsx
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import styled from "styled-components";
|
||||
import { FontBoldIcon, FontItalicIcon, StrikethroughIcon, ArrowLeftIcon, ArrowRightIcon } from "@radix-ui/react-icons";
|
||||
|
||||
export const ToolbarButtons = ({ editor }: any) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ToolButtonsWrapper>
|
||||
<ToolBtn onClick={() => editor.chain().focus().undo().run()}>
|
||||
<ArrowLeftIcon />
|
||||
</ToolBtn>
|
||||
<ToolBtn onClick={() => editor.chain().focus().redo().run()}>
|
||||
<ArrowRightIcon />
|
||||
</ToolBtn>
|
||||
<ToolBtn onClick={() => editor.chain().focus().toggleBold().run()} className={editor.isActive("bold") ? "is-active" : ""}>
|
||||
<FontBoldIcon />
|
||||
</ToolBtn>
|
||||
<ToolBtn onClick={() => editor.chain().focus().toggleItalic().run()} className={editor.isActive("italic") ? "is-active" : ""}>
|
||||
<FontItalicIcon />
|
||||
</ToolBtn>
|
||||
<ToolBtn onClick={() => editor.chain().focus().toggleStrike().run()} className={editor.isActive("strike") ? "is-active" : ""}>
|
||||
<StrikethroughIcon />
|
||||
</ToolBtn>
|
||||
<ToolSelect onChange={(e) => editor.chain().focus().toggleHeading({ level: parseInt(e.target.value) }).run() }>
|
||||
<option value="1">Heading 1</option>
|
||||
<option value="2">Heading 2</option>
|
||||
<option value="3">Heading 3</option>
|
||||
<option value="4">Heading 4</option>
|
||||
<option value="5">Heading 5</option>
|
||||
<option value="6">Heading 6</option>
|
||||
</ToolSelect>
|
||||
</ToolButtonsWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const ToolButtonsWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: left;
|
||||
justify-content: left;
|
||||
`;
|
||||
|
||||
const ToolBtn = styled.div`
|
||||
display: flex;
|
||||
background: rgba(217, 217, 217, 0.24);
|
||||
border-radius: 6px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
padding: 5px;
|
||||
font-size: 5px;
|
||||
margin-right: 5px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&.is-active {
|
||||
background: rgba(176, 176, 176, 0.5);
|
||||
|
||||
&:hover {
|
||||
background: rgba(139, 139, 139, 0.5);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgba(217, 217, 217, 0.48);
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
const ToolSelect = styled.select`
|
||||
display: flex;
|
||||
background: rgba(217, 217, 217, 0.24);
|
||||
border-radius: 6px;
|
||||
width: 100px;
|
||||
border: none;
|
||||
height: 25px;
|
||||
padding: 5px;
|
||||
font-size: 11px;
|
||||
font-family: "DM Sans";
|
||||
margin-right: 5px;
|
||||
`;
|
||||
|
|
@ -12,15 +12,16 @@ export interface Auth {
|
|||
isLoading: boolean;
|
||||
}
|
||||
|
||||
|
||||
const AuthProvider = (props: any) => {
|
||||
const router = useRouter();
|
||||
const [auth, setAuth] = React.useState<Auth>({ access_token: "", isAuthenticated: false, userInfo: {}, isLoading: true });
|
||||
|
||||
async function checkRefreshToken() {
|
||||
let data = await getRefreshToken();
|
||||
if (data) {
|
||||
return data.access_token;
|
||||
}
|
||||
}
|
||||
|
||||
async function checkAuth() {
|
||||
try {
|
||||
|
|
@ -33,7 +34,7 @@ const AuthProvider = (props: any) => {
|
|||
setAuth({ access_token, isAuthenticated: true, userInfo, isLoading });
|
||||
|
||||
// if user is authenticated and tries to access login or signup page, redirect to home
|
||||
if(NON_AUTHENTICATED_ROUTES.includes(router.pathname)) {
|
||||
if (NON_AUTHENTICATED_ROUTES.includes(router.pathname)) {
|
||||
router.push("/");
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { HeaderProfileBox } from "../../security/HeaderProfileBox";
|
||||
import { HeaderProfileBox } from "../../Security/HeaderProfileBox";
|
||||
import learnhouseIcon from "public/learnhouse_icon.png";
|
||||
import learnhouseLogo from "public/learnhouse_logo.png";
|
||||
import Link from "next/link";
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import React from "react";
|
||||
import Head from "next/head";
|
||||
import styled from "styled-components";
|
||||
import AuthProvider from "../security/AuthProvider";
|
||||
import AuthProvider from "../Security/AuthProvider";
|
||||
import { motion } from "framer-motion";
|
||||
import { Menu } from "./elements/Menu";
|
||||
import { Menu } from "./Elements/Menu";
|
||||
|
||||
const Layout = (props: any) => {
|
||||
const variants = {
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
import React from "react";
|
||||
import { useEditor, EditorContent } from "@tiptap/react";
|
||||
import StarterKit from "@tiptap/starter-kit";
|
||||
import Collaboration from "@tiptap/extension-collaboration";
|
||||
import CollaborationCursor from "@tiptap/extension-collaboration-cursor";
|
||||
import { AuthContext } from "../security/AuthProvider";
|
||||
|
||||
interface EditorWithOptionsProps {
|
||||
content: string;
|
||||
ydoc: any;
|
||||
provider: any;
|
||||
setContent: (content: string) => void;
|
||||
}
|
||||
|
||||
function EditorWithOptions(props: EditorWithOptionsProps) {
|
||||
const auth: any = React.useContext(AuthContext);
|
||||
|
||||
const MenuBar = ({ editor }: any) => {
|
||||
if (!editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleBold().run()}
|
||||
disabled={!editor.can().chain().focus().toggleBold().run()}
|
||||
className={editor.isActive("bold") ? "is-active" : ""}
|
||||
>
|
||||
bold
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleItalic().run()}
|
||||
disabled={!editor.can().chain().focus().toggleItalic().run()}
|
||||
className={editor.isActive("italic") ? "is-active" : ""}
|
||||
>
|
||||
italic
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleStrike().run()}
|
||||
disabled={!editor.can().chain().focus().toggleStrike().run()}
|
||||
className={editor.isActive("strike") ? "is-active" : ""}
|
||||
>
|
||||
strike
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleCode().run()}
|
||||
disabled={!editor.can().chain().focus().toggleCode().run()}
|
||||
className={editor.isActive("code") ? "is-active" : ""}
|
||||
>
|
||||
code
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().unsetAllMarks().run()}>clear marks</button>
|
||||
<button onClick={() => editor.chain().focus().clearNodes().run()}>clear nodes</button>
|
||||
<button onClick={() => editor.chain().focus().setParagraph().run()} className={editor.isActive("paragraph") ? "is-active" : ""}>
|
||||
paragraph
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
|
||||
className={editor.isActive("heading", { level: 1 }) ? "is-active" : ""}
|
||||
>
|
||||
h1
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
|
||||
className={editor.isActive("heading", { level: 2 }) ? "is-active" : ""}
|
||||
>
|
||||
h2
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}
|
||||
className={editor.isActive("heading", { level: 3 }) ? "is-active" : ""}
|
||||
>
|
||||
h3
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 4 }).run()}
|
||||
className={editor.isActive("heading", { level: 4 }) ? "is-active" : ""}
|
||||
>
|
||||
h4
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 5 }).run()}
|
||||
className={editor.isActive("heading", { level: 5 }) ? "is-active" : ""}
|
||||
>
|
||||
h5
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 6 }).run()}
|
||||
className={editor.isActive("heading", { level: 6 }) ? "is-active" : ""}
|
||||
>
|
||||
h6
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().toggleBulletList().run()} className={editor.isActive("bulletList") ? "is-active" : ""}>
|
||||
bullet list
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().toggleOrderedList().run()} className={editor.isActive("orderedList") ? "is-active" : ""}>
|
||||
ordered list
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().toggleCodeBlock().run()} className={editor.isActive("codeBlock") ? "is-active" : ""}>
|
||||
code block
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().toggleBlockquote().run()} className={editor.isActive("blockquote") ? "is-active" : ""}>
|
||||
blockquote
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().setHorizontalRule().run()}>horizontal rule</button>
|
||||
<button onClick={() => editor.chain().focus().setHardBreak().run()}>hard break</button>
|
||||
<button onClick={() => editor.chain().focus().undo().run()} disabled={!editor.can().chain().focus().undo().run()}>
|
||||
undo
|
||||
</button>
|
||||
<button onClick={() => editor.chain().focus().redo().run()} disabled={!editor.can().chain().focus().redo().run()}>
|
||||
redo
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const editor : any = useEditor({
|
||||
extensions: [
|
||||
StarterKit.configure({
|
||||
// The Collaboration extension comes with its own history handling
|
||||
history: false,
|
||||
}),
|
||||
// Register the document with Tiptap
|
||||
Collaboration.configure({
|
||||
document: props.ydoc,
|
||||
}),
|
||||
// Register the collaboration cursor extension
|
||||
CollaborationCursor.configure({
|
||||
provider: props.provider,
|
||||
user: {
|
||||
name: auth.userInfo.username,
|
||||
color: "#f783ac",
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
content: props.content,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
File <button onClick={() => props.setContent(editor.getJSON())}>save</button>
|
||||
<br /><hr />
|
||||
<MenuBar editor={editor} />
|
||||
<EditorContent editor={editor} style={{ backgroundColor: "white" }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditorWithOptions;
|
||||
|
|
@ -4,7 +4,7 @@ import styled from "styled-components";
|
|||
import learnhouseBigIcon from "public/learnhouse_bigicon.png";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { PreAlphaLabel } from "../components/ui/Layout";
|
||||
import { PreAlphaLabel } from "../components//UI/Layout";
|
||||
|
||||
const Home: NextPage = () => {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import Router from "next/router";
|
||||
import React from "react";
|
||||
import { Header } from "../components/ui/Header";
|
||||
import Layout from "../components/ui/Layout";
|
||||
import { Title } from "../components/ui/styles/Title";
|
||||
import { Header } from "../components//UI/Header";
|
||||
import Layout from "../components//UI/Layout";
|
||||
import { Title } from "../components//UI/Elements/Styles/Title";
|
||||
import { loginAndGetToken } from "../services/auth/auth";
|
||||
|
||||
const Login = () => {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import React from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import styled from "styled-components";
|
||||
import { Header } from "../../../../../../components/ui/Header";
|
||||
import Layout from "../../../../../../components/ui/Layout";
|
||||
import { Title } from "../../../../../../components/ui/styles/Title";
|
||||
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
|
||||
import { initialData, initialData2 } from "../../../../../../components/drags/data";
|
||||
import Chapter from "../../../../../../components/drags/Chapter";
|
||||
import { Header } from "../../../../../../components//UI/Header";
|
||||
import Layout from "../../../../../../components//UI/Layout";
|
||||
import { Title } from "../../../../../../components//UI/Elements/Styles/Title";
|
||||
import { DragDropContext, Droppable } from "react-beautiful-dnd";
|
||||
import { initialData, initialData2 } from "../../../../../../components/Drags/data";
|
||||
import Chapter from "../../../../../../components/Drags/Chapter";
|
||||
import { createChapter, deleteChapter, getCourseChaptersMetadata, updateChaptersMetadata } from "../../../../../../services/courses/chapters";
|
||||
import { useRouter } from "next/router";
|
||||
import NewChapterModal from "../../../../../../components/modals/CourseEdit/NewChapter";
|
||||
import NewElementModal from "../../../../../../components/modals/CourseEdit/NewElement";
|
||||
import NewChapterModal from "../../../../../../components/Modals/CourseEdit/NewChapter";
|
||||
import NewElementModal from "../../../../../../components/Modals/CourseEdit/NewElement";
|
||||
import { createElement, createFileElement } from "../../../../../../services/courses/elements";
|
||||
|
||||
function CourseEdit() {
|
||||
|
|
|
|||
|
|
@ -1,48 +1,54 @@
|
|||
import { default as React, useEffect, useRef } from "react";
|
||||
|
||||
import Layout from "../../../../../../../components/ui/Layout";
|
||||
import { Title } from "../../../../../../../components/ui/styles/Title";
|
||||
import Layout from "../../../../../../../components//UI/Layout";
|
||||
import { Title } from "../../../../../../../components//UI/Elements/Styles/Title";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useRouter } from "next/router";
|
||||
import { getElement } from "../../../../../../../services/courses/elements";
|
||||
import AuthProvider from "../../../../../../../components/Security/AuthProvider";
|
||||
import EditorWrapper from "../../../../../../../components/Editor/EditorWrapper";
|
||||
import { getCourseMetadata } from "../../../../../../../services/courses/courses";
|
||||
|
||||
// Workaround (Next.js SSR doesn't support tip tap editor)
|
||||
const Editor: any = dynamic(() => import("../../../../../../../components/editor/Editor") as any, {
|
||||
const Editor: any = dynamic(() => import("../../../../../../../components/Editor/EditorWrapper") as any, {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
function EditElement() {
|
||||
const router = useRouter();
|
||||
const { elementid } = router.query;
|
||||
const { elementid, courseid } = router.query;
|
||||
const [element, setElement] = React.useState<any>({});
|
||||
const [courseInfo, setCourseInfo] = React.useState({}) as any;
|
||||
const [isLoading, setIsLoading] = React.useState(true);
|
||||
|
||||
async function fetchElementData() {
|
||||
const element = await getElement("element_" + elementid);
|
||||
setElement(element);
|
||||
}
|
||||
|
||||
async function fetchCourseInfo() {
|
||||
const course = await getCourseMetadata("course_" + courseid);
|
||||
setCourseInfo(course);
|
||||
}
|
||||
|
||||
async function fetchAllData() {
|
||||
await fetchElementData();
|
||||
await fetchCourseInfo();
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (router.isReady) {
|
||||
fetchElementData();
|
||||
fetchAllData();
|
||||
}
|
||||
return () => {};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [router.isReady]);
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Title>Edit : {element.name} </Title>
|
||||
<br />
|
||||
{isLoading ? (
|
||||
<div>Loading...</div>
|
||||
) : (
|
||||
<div>
|
||||
<Editor element={element} content={element.content}></Editor>
|
||||
</div>
|
||||
)}
|
||||
</Layout>
|
||||
<AuthProvider>
|
||||
{isLoading ? <div>Loading...</div> : <EditorWrapper course={courseInfo} element={element} content={element.content}></EditorWrapper>}
|
||||
</AuthProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import Text from "@tiptap/extension-text";
|
|||
import { generateHTML } from "@tiptap/html";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useMemo } from "react";
|
||||
import Layout from "../../../../../../../components/ui/Layout";
|
||||
import Layout from "../../../../../../../components//UI/Layout";
|
||||
import { getElement } from "../../../../../../../services/courses/elements";
|
||||
import { getBackendUrl } from "../../../../../../../services/config";
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import Link from "next/link";
|
|||
import { useRouter } from "next/router";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import Layout from "../../../../../components/ui/Layout";
|
||||
import Layout from "../../../../../components//UI/Layout";
|
||||
import { getAPIUrl, getBackendUrl } from "../../../../../services/config";
|
||||
import { getCourse, getCourseMetadata } from "../../../../../services/courses/courses";
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import Link from "next/link";
|
|||
import { useRouter } from "next/router";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { Header } from "../../../../components/ui/Header";
|
||||
import Layout from "../../../../components/ui/Layout";
|
||||
import { Title } from "../../../../components/ui/styles/Title";
|
||||
import { Header } from "../../../../components//UI/Header";
|
||||
import Layout from "../../../../components//UI/Layout";
|
||||
import { Title } from "../../../../components//UI/Elements/Styles/Title";
|
||||
import { getBackendUrl } from "../../../../services/config";
|
||||
import { deleteCourseFromBackend, getOrgCourses } from "../../../../services/courses/courses";
|
||||
import { getOrganizationContextInfo } from "../../../../services/orgs";
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useRouter } from "next/router";
|
||||
import React from "react";
|
||||
import { Header } from "../../../../../components/ui/Header";
|
||||
import Layout from "../../../../../components/ui/Layout";
|
||||
import { Title } from "../../../../../components/ui/styles/Title";
|
||||
import { Header } from "../../../../../components//UI/Header";
|
||||
import Layout from "../../../../../components//UI/Layout";
|
||||
import { Title } from "../../../../../components//UI/Elements/Styles/Title";
|
||||
import { createNewCourse } from "../../../../../services/courses/courses";
|
||||
import { getOrganizationContextInfo } from "../../../../../services/orgs";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import Layout from "../../../components/ui/Layout";
|
||||
import { Title } from "../../../components/ui/styles/Title";
|
||||
import { Header } from "../../../components/ui/Header";
|
||||
import Layout from "../../../components//UI/Layout";
|
||||
import { Title } from "../../../components//UI/Elements/Styles/Title";
|
||||
import { Header } from "../../../components//UI/Header";
|
||||
import Link from "next/link";
|
||||
|
||||
const OrgHomePage = () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import Link from "next/link";
|
||||
import React from "react";
|
||||
import Layout from "../../components/ui/Layout";
|
||||
import { Title } from "../../components/ui/styles/Title";
|
||||
import Layout from "../../components//UI/Layout";
|
||||
import { Title } from "../../components//UI/Elements/Styles/Title";
|
||||
import { deleteOrganizationFromBackend, getUserOrganizations } from "../../services/orgs";
|
||||
|
||||
const Organizations = () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import Layout from "../../components/ui/Layout";
|
||||
import { Title } from "../../components/ui/styles/Title";
|
||||
import Layout from "../../components//UI/Layout";
|
||||
import { Title } from "../../components//UI/Elements/Styles/Title";
|
||||
import { createNewOrganization } from "../../services/orgs";
|
||||
|
||||
const Organizations = () => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import { Header } from "../components/ui/Header";
|
||||
import Layout from "../components/ui/Layout";
|
||||
import { Title } from "../components/ui/styles/Title";
|
||||
import { Header } from "../components//UI/Header";
|
||||
import Layout from "../components//UI/Layout";
|
||||
import { Title } from "../components//UI/Elements/Styles/Title";
|
||||
import { signup } from "../services/auth/auth";
|
||||
|
||||
const SignUp = () => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { initialData } from "../../components/drags/data";
|
||||
import { initialData } from "../../components/Drags/data";
|
||||
import { getAPIUrl } from "../config";
|
||||
|
||||
export async function getCourseChaptersMetadata(course_id: any) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,6 @@
|
|||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "components/drags/chapter.stsx"],
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue